//
// Created by ta&to on 2022/9/28.
//

#include "tcp_connect.h"
#ifdef SOCKET_SERVER_EPOLL
static void _init_epoll(log_t *logt, struct tcp_socket_event_loop_s *hset){
    hset->epoll_ev_fds = (struct epoll_event *) tcp_calloc(hset->max_link * sizeof(struct epoll_event *));
    hset->func_handler->data = (char*) tcp_calloc(hset->func_handler->data_len);
    hset->func_handler->sin_size = sizeof(hset->func_handler->client_addr);
}
static int _create_epoll(log_t *logt, struct tcp_socket_event_loop_s *hset) {
    hset->epoll_fd = epoll_create(hset->maxfd);
    if(hset->epoll_fd < 0)
    {
        error(logt, "epoll create error[%s]", strerror(errno));
    }
    return hset->epoll_fd;
}
static void _add_event(log_t *logt, struct tcp_socket_event_loop_s *hset, int new_fd, int type, int mask) {
    struct epoll_event ev;
    ev.events = 0;
    if (mask & READABLE) ev.events |= EPOLLIN;
    if (mask & WRITABLE) ev.events |= EPOLLOUT;
    ev.data.fd = new_fd;
    if(epoll_ctl(hset->epoll_fd, type, new_fd, &ev)==-1)//往内核事件表中注册fd上的事件
    {
        error(logt, "epoll_ctl add error [%s]", strerror(errno));
        exit(0);
    }
}
// 获取一个已完成三次握手的连接
static int _conn_handler(log_t *logt, struct tcp_socket_event_loop_s *hset)
{
    int client_fd = Accept(logt, hset->socket_fd, (struct sockaddr *)&hset->func_handler->client_addr,&hset->func_handler->sin_size);
    assert(client_fd != -1);
    info(logt, "one client link success[%d]", client_fd);
    return client_fd;
}
int __monitor_rm_fd(log_t *logt, struct tcp_socket_event_loop_s *hset, int fd)
{
    if(epoll_ctl(hset->epoll_fd, EPOLL_CTL_DEL, fd,NULL) == -1)
    {
        error(logt, "epoll_ctl del error:[%s]", strerror(errno));
    }
    return fd;
}
static void _offline_client(log_t *logt, struct tcp_socket_event_loop_s *hset, int fd) {
    info(logt, "A Clien is close[%d] epoll_fd[%d]", fd, hset->epoll_fd);
    close(fd);
}

void Close_Client(log_t *logt, struct tcp_socket_event_loop_s *hset, int fd) {
    _offline_client(logt, hset, fd);
}

void Close_link(log_t *logt, struct tcp_socket_event_loop_s *hset, int fd) {
    info(logt, "A Clien is close[%d] epoll_fd[%d]", fd, hset->epoll_fd);
    __monitor_rm_fd(logt, hset, fd);
    close(fd);
}

void _fd_handler(log_t *logt, struct tcp_socket_event_loop_s *hset, int n) {
    for (int i = 0; i < n; ++i) {
        struct epoll_event ev = hset->epoll_ev_fds[i];
        if(ev.data.fd == hset->socket_fd){      // 特殊处理 得到一个新的客户端链接
            int client_fd = _conn_handler(logt, hset);
            _add_event(logt, hset, client_fd, EPOLL_CTL_ADD, READABLE);
        }
        else if(ev.events & EPOLLRDHUP)//EPOLLRDHUP事件 判断对端是否关闭（当socket接收到对方关闭连接时的请求之后触发，有可能是TCP连接被对方关闭，也有可能是对方关闭了写操作。）
        {
            debug(logt, "client[%d] be going to close\n",ev.data.fd);
            _offline_client(logt, hset, ev.data.fd);
        }
        else if(ev.events & EPOLLIN)//EPOLLIN事件，连接到达；有数据来临；
        {
            hset->current_fd = ev.data.fd;
            assert(hset->func_handler);
            hset->func_handler->fd = ev.data.fd;
            hset->func_handler->func_read(hset, __monitor_rm_fd(logt, hset, ev.data.fd));
        }
        else
        {
            error(logt, "error ev.events data[%d]", ev.events);
        }
    }
}

static int _epoll_poll(log_t *logt, struct tcp_socket_event_loop_s *hset) {
    while(1) {
        struct epoll_event events[ hset->max_link];
        hset->epoll_ev_fds = events;
        int n=epoll_wait(hset->epoll_fd, hset->epoll_ev_fds, hset->max_link,
                         (ISNOTNULL(hset->tvp) ? (hset->tvp->tv_sec*1000 + (hset->tvp->tv_usec + 999)/1000) : -1));//一段超时事件内等待一组文件描述符上的事件
        if(n<0)
        {
            error(logt, "epoll_wait error [%s]", strerror(errno));
            continue;
        }
        else if(n==0)
        {
            warn(logt, "已超时\n");
            continue;
        }
        else {
            _fd_handler(logt, hset, n);
        }
    }
}
void Epoll_do(log_t *logt, struct tcp_socket_event_loop_s *hset) {
    int n;
    _init_epoll(logt, hset);
    n = _create_epoll(logt, hset);
    assert(n!=-1);
    _add_event(logt, hset, hset->socket_fd, EPOLL_CTL_ADD, READABLE);
    _epoll_poll(logt, hset);
}
int Epoll(log_t *logt, struct tcp_socket_event_loop_s *hset) {
#if !defined(__linux__)
    error(logt, "Platform no Support Epoll");
#else
    Epoll_do(logt, hset);
    free(hset->epoll_ev_fds);
#endif
}
#endif